package org.shoukaiseki.aqoeous.processor;


import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageSerializable;
import org.shoukaiseki.aqoeous.config.SpringContextUtil;
import org.shoukaiseki.aqoeous.config.Validator;
import org.shoukaiseki.aqoeous.constants.LoggerConstants;
import org.shoukaiseki.aqoeous.exception.ResultException;
import org.shoukaiseki.aqoeous.handler.AqoeousTriggerHandler;
import org.shoukaiseki.aqoeous.model.AqoeousModel;
import org.shoukaiseki.aqoeous.model.CustomQO;
import org.shoukaiseki.aqoeous.model.RelationshipQO;
import org.shoukaiseki.aqoeous.model.TriggerHandlerParam;
import org.shoukaiseki.aqoeous.scan.AqoeousScan;
import org.shoukaiseki.aqoeous.service.IAqoeous;
import org.shoukaiseki.aqoeous.service.TokenUserService;
import org.shoukaiseki.aqoeous.utils.CollectionUtils;
import org.shoukaiseki.common.exception.MessageVirtualException;
import org.shoukaiseki.common.utils.StringUtils;
import org.slf4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.validation.BeanPropertyBindingResult;

import javax.annotation.Resource;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.shoukaiseki.aqoeous.model.RelationshipQO.*;


/**
 * org.shoukaiseki.aqoeous.processor.CustomQOProcessor
 * <br>
 *
 * @author shoukaiseki ブログ http://www.shoukaiseki.top/<br> E-メール jiang28555@Gmail.com<br>
 * @date 2020-12-17 11:18:42<br>
 */
@Component
public class CustomQOProcessor {

    final static Logger logger= LoggerConstants.AQOEOUS_LOGGER;

    public static final String CUSTOM_AQOEOUS="customAqoeous";

    public static final String RESULT_NAME_MAIN = "main";

    @Resource
    AqoeousScan aqoeousScan;

    @Resource
    Validator validator;

    @Resource
    TokenUserService tokenUserService;


//    @Deprecated
//    public Map<String, PageSerializable> parserCustomQO(Map<String, CustomQO> customAqoeous, Map<String, Object> resultMap )  {
//        if(customAqoeous==null||customAqoeous.isEmpty()){
//            return null;
//        }
//
////        customAqoeous.entrySet().stream().sorted(new Ordering<Map.Entry<String, CustomQO>>() {
////            @Override
////            public int compare(Map.Entry<String, CustomQO> left, Map.Entry<String, CustomQO> right) {
////                return 0;
////            }
////        });
//
//        Map<String,PageSerializable> customsPage= new HashMap<>();
//        resultMap.put(CustomQOProcessor.CUSTOM_AQOEOUS,customsPage);
//
//        for (Map.Entry<String,CustomQO> entry:customAqoeous.entrySet() ) {
//            CustomQO customQO = entry.getValue();
//            String resultName = entry.getKey();
//
////            Method method = ReflectionUtils.findMethod(bean.getClass(), "qo", aqoeousModel.getQoclass());
////            Object[] param1 = new Object[1];
////            param1[0]=;
////            ReflectionUtils.invokeMethod(method, bean, param1);
//            PageSerializable o = customQOParser(resultName, customQO, resultMap);
//            customsPage.put(resultName, o);
//        }
//
//        return customsPage;
//    }

    public  <T> T parserCustomQOSingle(CustomQO customQO) {
        customQO.setResultName(RESULT_NAME_MAIN);
        List list = (List) Stream.of(customQO).collect(Collectors.toList());
        Map<String, Object> qoMapList = this.parserCustomQO(list);
        return (T)qoMapList.get(RESULT_NAME_MAIN);
    }

    public Map<String, Object> parserCustomQO(CustomQO... customQOs) {
        List list = (List) Stream.of(customQOs).collect(Collectors.toList());
        Map<String, Object> qoMapList = this.parserCustomQO(list);
        return qoMapList;
    }

    public Map<String,Object> parserCustomQO(List<CustomQO> customAqoeous  )  {
        if(customAqoeous==null||customAqoeous.isEmpty()){
            return null;
        }

        HashMap<String, Object> resultMap = new HashMap<>();


        for (CustomQO customQO:customAqoeous) {
            String resultName = customQO.getResultName();

//            Method method = ReflectionUtils.findMethod(bean.getClass(), "qo", aqoeousModel.getQoclass());
//            Object[] param1 = new Object[1];
//            param1[0]=;
//            ReflectionUtils.invokeMethod(method, bean, param1);
            PageSerializable o = customQOParser(resultName, customQO, resultMap);
            resultMap.put(resultName, o);

            if(customQO.isMergeParent()){
                if (customQO.isPageTotalMergeParent()) {
                    resultMap.put(resultName,o.getTotal());
                    continue;
                }
                if(customQO.isListFirstMergeParent()){
                    Object firstObj = o.getList().stream().findFirst().orElse(null);
                    resultMap.put(resultName,firstObj);
                }else if(customQO.isListMergeParent()){
                    resultMap.put(resultName,o.getList());
                }else{
                    resultMap.put(resultName,o);
                }
            }else{
                resultMap.put(resultName, o);
            }
        }

        return resultMap;
    }



    public PageSerializable customQOParser(String resultName,CustomQO customQO,Map<String,Object> resultMap){
        String qoName = customQO.getQoName();
//        String qoJson = customQO.getQoJson();
        JSONObject qoJson = customQO.getQoJson();
        logger.debug("customQO={}",customQO);
        AqoeousModel aqoeousModel = AqoeousScan.getAqoeousModelMap().get(qoName);
        if (logger.isDebugEnabled()) {
            logger.debug("aqoeousModel={}", JSONObject.toJSONString(aqoeousModel));
        }
        if(aqoeousModel==null){
            throw new RuntimeException("查询器"+qoName+"不存在");
        }

        Object bean = SpringContextUtil.getBean(aqoeousModel.getBeanName());

        List<RelationshipQO> relationshipList = customQO.getRelationships();
        AqoeousTriggerHandler aqoeousTriggerHandler = customQO.getTriggerHandler();
        if(aqoeousTriggerHandler==null){
            String triggerBean = customQO.getTriggerBean();
            aqoeousTriggerHandler =new AqoeousTriggerHandler() { };
            if(StringUtils.isNotEmpty(triggerBean)){
                try{
                    aqoeousTriggerHandler = SpringContextUtil.getBean(triggerBean, AqoeousTriggerHandler.class);
                }catch (Throwable t){
                    logger.error("triggerBean 名称不存在,或者未实现 AqoeousTriggerHandler 接口",t);
                }
            }
        }

        PageSerializable page = aqoeousTriggerHandler.customQOParserBefore(new TriggerHandlerParam()
                .setResultMap(resultMap)
                .setResultName(resultName)
                .setCustomQOProcessor(this)
        );
        if(page!=null){
            return page;
        }

        List resultList= new ArrayList();
        long total=0;
        if(relationshipList !=null&&!relationshipList.isEmpty()){

            Object qo = qoJson.toJavaObject(aqoeousModel.getQoclass());
            for (int index=0;index< relationshipList.size();index++) {
                RelationshipQO relationshipQO =relationshipList.get(index);
                validator.validate(relationshipQO,new BeanPropertyBindingResult(relationshipQO,resultName+".relationshipQO-"+index),true);
//                Object qo = JSONObject.parseObject(qoJson, aqoeousModel.getQoclass());
                String[] sourceDatas = relationshipQO.getSourceDatas();
                String paramName = relationshipQO.getParamName();
                Field field = ReflectionUtils.findField(qo.getClass(), paramName);

                if(field==null){
                    throw new RuntimeException("关联查询中的paramName不存在["+resultName+".relationshipQO-"+index+"."+paramName+"]");
                }

                Set paramDataSet= new HashSet();
                for (String sourceData:sourceDatas) {
                    Set paramValues = relationshipParser(resultMap,relationshipQO, sourceData,resultName+".relationshipQO-"+index);
                    logger.debug("field.name={},type={}",field.getName(),field.getType());
                    logger.debug("{}asus={}",LoggerConstants.PREFIX,paramValues);
                    paramDataSet.addAll(paramValues);
                }

                if(logger.isInfoEnabled()){
                    logger.info("{}{}.relationshipParser{}={}", LoggerConstants.PREFIX,resultName,index,JSONObject.toJSONString(paramDataSet));
                }

                try {
                    if (logger.isDebugEnabled()) {
                        logger.debug("paramDataSet={}",JSONObject.toJSONString(paramDataSet));
                    }
                    org.apache.commons.beanutils.BeanUtils.populate(qo,Map.of(paramName,paramDataSet));
                } catch (Throwable e) {
                    throw new RuntimeException("出错了",e);
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("{}qo={}",LoggerConstants.PREFIX,JSONObject.toJSONString(qo));
                }
            }

            tokenUserService.initSO(qo);
            validator.validate(qo,new BeanPropertyBindingResult(qo,resultName+qoName));

            if(bean instanceof IAqoeous){
                List<?> list = ((IAqoeous) bean).aqoeous(qo);
                if(list!=null){
                    CollectionUtils.addAll(resultList,list.iterator());
                    if(list instanceof Page){
                        total+=((Page<?>) list).getTotal();
                    }
                }

            }else{
                resultList=null;
                logger.debug("bean={}",bean);
                throw new ResultException("bean={}未实现 IAqoeous 接口").format(bean);
            }
        }else{
//            Object qo = JSONObject.parseObject(qoJson, aqoeousModel.getQoclass());
            Object qo = qoJson.toJavaObject(aqoeousModel.getQoclass());
            tokenUserService.initSO(qo);
            validator.validate(qo,new BeanPropertyBindingResult(qo,qoName));

            if(bean instanceof IAqoeous){
                resultList = ((IAqoeous) bean).aqoeous(qo);
                if(resultList instanceof Page){
                    total=((Page) resultList).getTotal();
                }
            }else{
                resultList=null;
                logger.debug("bean={}",bean);
                throw new ResultException("bean={}未实现 IAqoeous 接口").format(bean);
            }
        }
        if(resultList!=null){
            List<CustomQO> qoChilds = customQO.getQoChilds();
            //始终进行循环处理
//            if((qoChilds==null||qoChilds.isEmpty())
//                    &&customQO.getIncludeFieldList()==null&&customQO.getExcludeFieldList()==null){
//                PageSerializable page = PageSerializable.of(resultList);
//                page.setTotal(total);
//                return page;
//            }
            List resultListTemp= new ArrayList();
            for (Object model : resultList) {
                //只包含某些字段,排除某些字段的时候,不对子表合并到主表的字段进行排除
                Set<String> childPropNameSet=new HashSet<>();
                JSONObject jo = null;
                try{
                    Object joTmp = JSONObject.toJSON(model);
                    if(joTmp instanceof JSONObject){
                        jo=(JSONObject)joTmp;
                    }else{
                        TriggerHandlerParam TriggerHandlerParam = new TriggerHandlerParam()
                                .setModel(joTmp)
                                .setRowType(1)
                                .setResultMap(resultMap);
                        Object resultTmp = aqoeousTriggerHandler.listAddBefore(TriggerHandlerParam);

                        resultListTemp.add(resultTmp);
                       continue;
                    }
                }catch (Throwable t){
                    logger.error("model={},customQO={}",model,JSONObject.toJSONString(customQO));
                    throw new RuntimeException(t);
                }
                HashMap<Object, Object> childMap = new HashMap<>();
                if (qoChilds!=null&&!qoChilds.isEmpty()) {
                    for (CustomQO qoChild : qoChilds) {
                        String childResultName = qoChild.getResultName();


                        PageSerializable o = customQOParser(childResultName, qoChild, jo);
                        if(qoChild.isMergeParent()){
                            if (qoChild.isPageTotalMergeParent()) {
                                jo.put(childResultName,o.getTotal());
                                continue;
                            }
                            if(qoChild.isListFirstMergeParent()){
                                Object firstObj = o.getList().stream().findFirst().orElse(null);
                                jo.put(childResultName,firstObj);
                            }else if(qoChild.isListMergeParent()){
                                jo.put(childResultName,o.getList());
                            }else{
                                jo.put(childResultName,o);
                            }
                            childPropNameSet.add(childResultName);
                        }else{
                            childMap.put(childResultName,o);

                            throw  new MessageVirtualException("aqoeousChild 已弃用");
                        }
                    }
                }
//                jo.put("aqoeousChild",childMap);

                if(customQO.getIncludeFieldList()==null&&customQO.getExcludeFieldList()==null){
                    TriggerHandlerParam TriggerHandlerParam = new TriggerHandlerParam()
                            .setRow(jo)
                            .setModel(model)
                            .setResultMap(resultMap);
                    Object resultTmp = aqoeousTriggerHandler.listAddBefore(TriggerHandlerParam);

                    resultListTemp.add(resultTmp);
                    continue;
                }

                JSONObject joTmp=new JSONObject();

                for (String fieldName : jo.keySet()) {
                    if (childPropNameSet.contains(fieldName)) {
                        joTmp.put(fieldName,jo.get(fieldName));
                    }
                   if(customQO.getIncludeFieldList()!=null){
                       if (customQO.getIncludeFieldList().contains(fieldName)) {
                           joTmp.put(fieldName,jo.get(fieldName));
                       }
                   }else{
                       if (!customQO.getExcludeFieldList().contains(fieldName)) {
                           joTmp.put(fieldName,jo.get(fieldName));
                       }
                   }
                }


                TriggerHandlerParam TriggerHandlerParam = new TriggerHandlerParam()
                        .setRow(joTmp)
                        .setModel(model)
                        .setResultMap(resultMap);
                Object resultTmp = aqoeousTriggerHandler.listAddBefore(TriggerHandlerParam);

                resultListTemp.add(resultTmp);

            }
            PageSerializable pageTmp = PageSerializable.of(resultListTemp);
            pageTmp.setTotal(total);
            return pageTmp;
        }
        return null;

    }

    public Set relationshipParser(Map<String,Object> resultMap,RelationshipQO relationshipQO,String sourceData,String objectName) {
        Set list = new HashSet();
        String[] split = sourceData.split("\\.");
        Object temp = resultMap;
        for (int i = 0; i < split.length; i++) {
            String fieldName = split[i];
            if (temp instanceof Map) {
                temp = ((Map) temp).get(fieldName);
                if(i==split.length-1){
                    list.addAll(valueToSet(temp));
                }
                continue;
            }

            if (temp instanceof PageSerializable) {
                temp = ((PageSerializable) temp).getList();
            }

            if (temp instanceof Collection) {
                list = new HashSet();
                for (Object source : (Collection) temp) {
                    PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), fieldName);
                    if (sourcePd != null) {
                        Method readMethod = sourcePd.getReadMethod();
                        if (readMethod != null &&
                                ClassUtils.isAssignable(readMethod.getReturnType(), readMethod.getReturnType())) {
                            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                readMethod.setAccessible(true);
                            }
                            try {
                                Object value = readMethod.invoke(source);
                                list.addAll(valueToSet(value));
                            } catch (Throwable e) {
                                throw new RuntimeException("出错了",e);
                            }

                        }
                    }else{
                        if(source instanceof Map) {
                            Object value = ((Map) source).get(fieldName);
                            list.addAll(valueToSet(value));
                        }else{
                            logger.error("{}i={},fieldName={},temp{}", LoggerConstants.PREFIX,i,fieldName, temp);

                            throw new ResultException("错误"+objectName + ".sourceDatas" + "[" + fieldName + "]");
                        }
                    }
                }
                //如果获取到的fieldName为List,则下次获取的字段名还是该名称重新循环一遍
                // 支持N级子表关联信息获取
                temp = list;
                if(logger.isDebugEnabled()){
                    logger.debug("temp={}",JSONObject.toJSONString(temp));
                }
                continue;
            }

            if (logger.isErrorEnabled()) {
                logger.error("{}i={},fieldName={},temp{}", LoggerConstants.PREFIX,i,fieldName, temp);
            }
            if(temp==null){
                if(i>0){
                    fieldName=split[i-1];
                }
                throw new RuntimeException("relationship名称错误,找不到对应的可用值"+sourceData+"-"+fieldName);
//                throw new ResultException(ResultException.DEFA_GROUP, 13).setParams(sourceData,fieldName);
            }
//            throw new ResultException(ResultException.DEFA_GROUP, 12).setParams(sourceData,fieldName);
            throw new RuntimeException("relationship名称错误,无法识别的类型值"+sourceData+"-"+fieldName);
        }
        String sourceType = relationshipQO.getSourceType();
        if(StringUtils.isEmpty(sourceType)){
            return list;
        }

        Set listTmp = new HashSet();

        for (Object tmp : list){
            if (StringUtils.equalsAny(sourceType,RELATIONSHIP_SOURCE_TYPE_STRING_ARRAY,RELATIONSHIP_SOURCE_TYPE_LONG_ARRAY,RELATIONSHIP_SOURCE_TYPE_INTEGER_ARRAY)) {
                String[] splitTmp = StringUtils.split(tmp.toString());
                if (splitTmp==null) {
                    continue;
                }
                for (String s : splitTmp) {
                    if (s==null) {
                        continue;
                    }
                    if (StringUtils.equalsAny(sourceType,RELATIONSHIP_SOURCE_TYPE_STRING_ARRAY)) {
                        listTmp.add(s);
                        continue;
                    }
                    if (StringUtils.equalsAny(sourceType,RELATIONSHIP_SOURCE_TYPE_LONG_ARRAY)) {
                        long l = Long.parseLong(s);
                        listTmp.add(l);
                        continue;
                    }
                    if (StringUtils.equalsAny(sourceType,RELATIONSHIP_SOURCE_TYPE_INTEGER_ARRAY)) {
                        int i = Integer.parseInt(s);
                        listTmp.add(i);
                        continue;
                    }
                }
            }
            throw new RuntimeException(StringUtils.format("relationship 元数据类型错误,无法识别的类型{}",sourceType));
        }
        return listTmp;
    }

    /**
     * 关联查询多个子表时,如果value为list,则将返回list内所有元素
     * @param value
     * @return
     */
    public Set valueToSet(Object value){
        Set set = new HashSet();
        if(value==null){
            return set;
        }
        if (value instanceof PageSerializable) {
            set.addAll(valueToSet(((PageSerializable) value).getList()));
        }else if(value instanceof Set){
            for (Object o : ((Set) value)) {
                set.addAll(valueToSet(o));
            }
        }else if(value instanceof List){
            for (Object o : ((List) value)) {
                set.addAll(valueToSet(o));
            }
        }else{
            set.add(value);
        }


        return set;
    }

//    public Set relationshipParser2(Map<String,Object> resultMap,String sourceData,String objectName) {
//        Set list = Sets.newHashSet();
//        String[] split = sourceData.split("\\.");
//        Object temp = resultMap;
//        for (int i = 0; i < split.length; i++) {
//            String fieldName = split[i];
//            if (temp instanceof Map) {
//                temp = ((Map) temp).get(fieldName);
//                if(i==split.length-1){
//                    list.add(temp);
//                }
//                continue;
//            }
//
//            if (temp instanceof PageSerializable) {
//                temp = ((PageSerializable) temp).getList();
//            }
//
//            if (temp instanceof Collection) {
//                list = Sets.newHashSet();
//                for (Object source : (Collection) temp) {
//                    PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), fieldName);
//                    if (sourcePd != null) {
//                        Method readMethod = sourcePd.getReadMethod();
//                        if (readMethod != null &&
//                                ClassUtils.isAssignable(readMethod.getReturnType(), readMethod.getReturnType())) {
//                            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
//                                readMethod.setAccessible(true);
//                            }
//                            try {
//                                Object value = readMethod.invoke(source);
//                                if (value instanceof PageSerializable) {
//                                    list.addAll(((PageSerializable) value).getList());
//                                } else {
//                                    list.add(value);
//                                }
//                            } catch (Throwable e) {
//                                throw new ResultException(4007, 2, e);
//                            }
//
//                        }
//                    }else{
//                        if(source instanceof Map) {
//                            Object value = ((Map) source).get(fieldName);
//                            list.add(value);
//                        }else if(source instanceof List){
//                            list.addAll(getListAttrValueToSet(source,fieldName,objectName));
//                        }else{
//                            logger.error("{}i={},fieldName={},temp{}", LoggerConstants.PREFIX,i,fieldName, temp);
//                            throw new ResultException(4007, 6).setParams(objectName + ".sourceDatas" + "[" + fieldName + "]");
//                        }
//                    }
//                }
//                //如果获取到的fieldName为List,则下次获取的字段名还是该名称重新循环一遍
//                // 支持N级子表关联信息获取
//                temp = list;
//                if(logger.isDebugEnabled()){
//                    logger.debug("temp={}",JSONObject.toJSONString(temp));
//                }
//                continue;
//            }
//
//            if (logger.isErrorEnabled()) {
//                logger.error("{}i={},fieldName={},temp{}", LoggerConstants.PREFIX,i,fieldName, temp);
//            }
//            if(temp==null){
//                if(i>0){
//                    fieldName=split[i-1];
//                }
//                throw new ResultException(ResultException.DEFA_GROUP, 13).setParams(sourceData,fieldName);
//            }
//            throw new ResultException(ResultException.DEFA_GROUP, 12).setParams(sourceData,fieldName);
//        }
//        return list;
//    }
//
//
//    public Set getListAttrValueToSet(Object temp,String fieldName,String objectName){
//        Set list = Sets.newHashSet();
//        if(temp instanceof Set) {
//            for(Object source:(Set)temp){
//                Set listTemp = getListAttrValueToSet(source, fieldName, objectName);
//                list.addAll(listTemp);
//            }
//        }else if(temp instanceof List){
//            for(Object source:(List)temp){
//                Set listTemp = getListAttrValueToSet(source,fieldName,objectName);
//                list.addAll(listTemp);
//            }
//        }else{
//            PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(temp.getClass(), fieldName);
//            if (sourcePd != null) {
//                Method readMethod = sourcePd.getReadMethod();
//                if (readMethod != null &&
//                        ClassUtils.isAssignable(readMethod.getReturnType(), readMethod.getReturnType())) {
//                    if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
//                        readMethod.setAccessible(true);
//                    }
//                    try {
//                        Object value = readMethod.invoke(temp);
//                        if (value instanceof PageSerializable) {
//                            list.addAll(((PageSerializable) value).getList());
//                        } else {
//                            list.add(value);
//                        }
//                    } catch (Throwable e) {
//                        throw new ResultException(4007, 2, e);
//                    }
//
//                }
//            } else {
//                if (temp instanceof Map) {
//                    Object value = ((Map) temp).get(fieldName);
//                    list.add(value);
//                } else {
//                    logger.error("{},fieldName={},temp{}", LoggerConstants.PREFIX,  fieldName, temp);
//                    throw new ResultException(4007, 6).setParams(objectName + ".sourceDatas" + "[" + fieldName + "]");
//                }
//            }
//        }
//        if (logger.isDebugEnabled()) {
//            logger.debug("temp={}", JSONObject.toJSONString(temp));
//        }
//        return list;
//    }


}

